
<!-- javascript 画心型线 -->
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=0" />
		<title>画图形测试</title>
        <style>
            *{
                margin: 0;
            }
        </style>
	</head>
<body>
	<canvas id="gycanvas" width="300" height="300" ></canvas>
	<script>

        function drawingBoard(){ //画板
            //画图时，默认过渡效果为0
            //绘制的必要参数

            this.queue= [];  //执行队列
            this.ctx= null;  //canvas对象
            this.width= 0;
            this.height= 0;
            setTimeout(()=>{ //顺序队列开始执行
                this.next()
            },0)
        }
        drawingBoard.prototype.next= function(){
            var fn= this.queue.shift();
            fn && fn()
        }
        drawingBoard.prototype.transition= function(speed,cb){  //过渡方法
            if(speed>0){
                let si=setInterval(()=>{cb(()=>{clearInterval(si)})},speed)
            } else{
                let isWhile= true
                while(isWhile){
                    cb(()=>{isWhile=false})
                }
            }
        }
        drawingBoard.prototype.init=function({id,width,height}={}){
            //铺满屏幕
            this.width = width || document.documentElement.clientWidth;
            this.height = height || document.documentElement.clientHeight-4;
            document.getElementById(id).setAttribute("width",this.width);
            document.getElementById(id).setAttribute("height",this.height);
            this.ctx= document.getElementById(id).getContext("2d");
            return this
        }
        drawingBoard.prototype.drawCircle= function({speed= 0, lineWidth= 1, r= 10, x, y,}={}){
            this.queue.push(()=>{
                var i =0,
                    opacity= 0.1
                if(typeof x === "undefined"){
                    x= r
                }
                if(typeof y === "undefined"){
                    y= r
                }
                this.ctx.beginPath();

                
                //设置边框样式以及填充颜色   
                this.ctx.lineWidth=lineWidth;   
                // this.ctx.fillStyle = "#fff";   
                // this.ctx.fill(); 
                this.transition(speed,(stop)=>{
                   
                    this.ctx.lineTo(Math.sin(Math.PI*i/180)*r+x,Math.cos(Math.PI*i/180)*r+y)
                    opacity= i/360
                    this.ctx.strokeStyle = `rgba(255, 80, 80, ${opacity})`;   
                    this.ctx.stroke(); 
                    i+=10
                    
                    if(i>360){
                        stop()
                        this.next()
                        return
                    }
                })
            })
            return this
        }
        drawingBoard.prototype.drawLine= function({speed= 0, lineWidth= 1, x1,y1,x2,y2}={}){
            this.queue.push(()=>{
                var xProg= x1,
                    yProg= y1,
                    opacity= 1,
                    xLen= x2-x1,
                    isUp= x2>x1,
                    isComplate= (xProg)=>isUp?xProg>x2:xProg<x2,
                    len= Math.sqrt(Math.pow((x2-x1),2)+Math.pow((y2-y1),2)), //两点间距离
                    lenRatio= 1/len //步长速率

                
                this.ctx.lineWidth=lineWidth;     
                this.ctx.beginPath();
                // this.ctx.moveTo(x1,y1);

                this.transition(speed,(stop)=>{
                    if(isComplate(xProg)){
                        stop()
                        this.next()
                        return
                    }
                    this.ctx.lineTo(xProg,yProg)
                    xProg+= lenRatio*xLen  //x随步长递增
                    yProg= (xProg-x1)/(x2-x1)*(y2-y1)+y1  //y的两点式变化
                    
                    this.ctx.strokeStyle = `rgba(255, 80, 80, ${opacity})`;
                    this.ctx.stroke(); 
                })
            })
            return this
        }
        drawingBoard.prototype.drawHeart= function({speed= 0, color='red',x,y,count=1}={}){
            this.queue.push(()=>{
                this.ctx.fillStyle= color;

                
                const getWidth= ()=>{
                    var w= Math.random()*(this.width-40)
                    return w<40?40:w
                }
                const getHeight= ()=>{
                    var h= Math.random()*(this.height-50)
                    return h<50?50:h
                }

                var x0 = x || getWidth();
                var y0 = y || getHeight();
                var x1,y1;

                // 单个心形宽高width: 74px;height: 99px;
                // 心形高宽比0.8125
                var wf= 6;
                var hf= wf*0.8125;

                var drow_=(t)=>{
                    x1=wf*Math.pow(Math.sin(t),3);
                    y1=hf*Math.cos(t)-5*Math.cos(t*2)-2*Math.cos(t*3)-Math.cos(t*4);
                    //增大心
                    x1=x1*wf;
                    y1=y1*wf;
                    
                    //画点
                    this.ctx.fillRect(x1+x0,y0-y1,1,1);
                }
                var t= -3;
                this.transition(speed,(stop)=>{
                    t=t+0.015;
                        drow_(t);
                        if(t>3){
                            if(count>1){
                                --count
                                t= -3
                                x0= getWidth();
                                y0= getHeight();
                            } else{
                                stop()
                                this.next()
                            }
                        }
                })
            })
            return this
        }
        drawingBoard.prototype.drawStar= function({speed= 0, color= "#000",lineWidth= 1,x,y}={}){
            this.queue.push(()=>{
                this.ctx.beginPath();  
                var i =0
                this.transition(speed,(stop)=>{
                    i++
                    //设置是个顶点的坐标，根据顶点制定路径    
                    this.ctx.lineTo(Math.cos((18+i*72)/180*Math.PI)*200+200,   
                                    -Math.sin((18+i*72)/180*Math.PI)*200+200); 

                    this.ctx.lineTo(Math.cos((54+i*72)/180*Math.PI)*80+200,   
                        -Math.sin((54+i*72)/180*Math.PI)*80+200);   
                
                    //设置边框样式以及填充颜色   
                    this.ctx.lineWidth= lineWidth;
                    this.ctx.fillStyle = "#fff";   
                    this.ctx.strokeStyle = color;   
                    this.ctx.fill();   
                    this.ctx.stroke(); 
                    if(i>5){
                        this.ctx.closePath()
                        stop()
                        this.next()
                    }
                })
            })
            return this
        }
        drawingBoard.prototype.drawStar2= function({speed= 0, color= "#000",x,y,size= 20}={}){
            this.queue.push(()=>{
                const drawStar =(ctx,width,height,r1,r2,angle,j)=>{
                    ctx.save();
                    ctx.translate(width/2, height/2);
                    ctx.rotate( -Math.PI/2 + j*2*angle);  //第一个图形。坐标轴旋转 -90.能直接得到两个顶点  (0,0)-(r1,0);
                    ctx.beginPath();
                    ctx.moveTo(0,0);
                    ctx.lineTo(r1,0);

                    // 绘制顶点
                    findAxisShort(angle,r2,ctx);
                    findAxisLong(angle,r1,ctx);
                    // 同心圆外侧大圆
                    function findAxisLong(angle,r1,ctx){
                        var longX = r1*Math.cos(angle*2);
                        var longY = r1*Math.sin(angle*2);
                        ctx.lineTo( longX, longY);
                    }
                    // 同心圆内侧小圆
                    function findAxisShort(angle,r2,ctx){
                        var shortX = r2*Math.cos(angle)
                        var shortY = r2*Math.sin(angle);
                        ctx.lineTo( shortX, shortY);
                    }
                    ctx.fill();
                    ctx.closePath();
                    ctx.restore();             
                }
                // 五角星可以看做是两个同心圆。被平分10份，10个顶点的连接
                let bigRadius = size / 2;
                let smallRadius = size / 4;
                let angle = Math.PI/5;
                this.ctx.fillStyle = color;

                var j= 0
                this.transition(speed,(stop)=>{
                    drawStar(this.ctx,size,size,bigRadius,smallRadius,angle,j);
                    j++
                    if(j>5){
                        stop()
                        this.next()
                    }
                })
            })
            return this
        }
        drawingBoard.prototype.drawScene= function({speed= 0, lineWidth= 1, r= 10, x, y,}={}){
            this.queue.push(()=>{
                var clockImage= new Image();
                clockImage.src = 'https://static.runoob.com/images/mix/125855_nnla_89964.png';
                var width= 500,
                    height= width,
                    clockRadius= width/2

                const draw= ()=>{
                    this.ctx.clearRect(0, 0, width, height);
                    
                    // get current time
                    var date = new Date();
                    var hours = date.getHours();
                    var minutes = date.getMinutes();
                    var seconds = date.getSeconds();
                    hours = hours > 12 ? hours - 12 : hours;
                    var hour = hours + minutes / 60;
                    var minute = minutes + seconds / 60;

                    // save current context
                    this.ctx.save();

                    // draw clock image (as background)
                    this.ctx.drawImage(clockImage, 0, 0, width, width);
                    this.ctx.translate(width / 2, height / 2);
                    this.ctx.beginPath();

                    // draw numbers
                    this.ctx.font = '36px Arial';
                    this.ctx.fillStyle = '#000';
                    this.ctx.textAlign = 'center';
                    this.ctx.textBaseline = 'middle';
                    for (var n = 1; n <= 12; n++) {
                        let theta = (n - 3) * (Math.PI * 2) / 12;
                        let x = clockRadius * 0.7 * Math.cos(theta);
                        let y = clockRadius * 0.7 * Math.sin(theta);
                        this.ctx.fillText(n, x, y);
                    }

                    // draw hour
                    this.ctx.save();
                    var theta = (hour - 3) * 2 * Math.PI / 12;
                    this.ctx.rotate(theta);
                    this.ctx.beginPath();
                    this.ctx.moveTo(-15, -5);
                    this.ctx.lineTo(-15, 5);
                    this.ctx.lineTo(clockRadius * 0.5, 1);
                    this.ctx.lineTo(clockRadius * 0.5, -1);
                    this.ctx.fill();
                    this.ctx.restore();

                    // draw minute
                    this.ctx.save();
                    var theta = (minute - 15) * 2 * Math.PI / 60;
                    this.ctx.rotate(theta);
                    this.ctx.beginPath();
                    this.ctx.moveTo(-15, -4);
                    this.ctx.lineTo(-15, 4);
                    this.ctx.lineTo(clockRadius * 0.8, 1);
                    this.ctx.lineTo(clockRadius * 0.8, -1);
                    this.ctx.fill();
                    this.ctx.restore();

                    // draw second
                    this.ctx.save();
                    var theta = (seconds - 15) * 2 * Math.PI / 60;
                    this.ctx.rotate(theta);
                    this.ctx.beginPath();
                    this.ctx.moveTo(-15, -3);
                    this.ctx.lineTo(-15, 3);
                    this.ctx.lineTo(clockRadius * 0.9, 1);
                    this.ctx.lineTo(clockRadius * 0.9, -1);
                    this.ctx.fillStyle = '#0f0';
                    this.ctx.fill();
                    this.ctx.restore();

                    this.ctx.restore();
                }
                clockImage.onload=(e)=>{
                    draw()
                    this.transition(1000,(stop)=>{
                        draw()
                        // stop()
                    })
                }
                this.next()
            })
            return this
        }
        drawingBoard.prototype.drawAnythingTest= function({speed= 0, lineWidth= 1, x,y}={}){
            this.queue.push(()=>{
                var i= 90,
                    r= 50,
                    opacity= 1

                
                this.ctx.lineWidth=lineWidth;     
                this.ctx.strokeStyle = `rgba(255, 80, 80, ${opacity})`;
                var path=[{x:10,y:10},{x:50,y:20},{x:70,y:100},{x:40,y:150}]
                var index=0,
                    count=0
                this.ctx.beginPath()

                this.transition(speed,(stop)=>{
                    // 画正弦线
                    this.ctx.lineTo(i+x,Math.cos(Math.PI*i/180)*r+y)
                    this.ctx.stroke(); 
                    i+=10
                    if(i>1080){
                        stop()
                        this.next()
                        return
                    }
                    // 画正弦线 end

                    // 画重叠线
                    // this.ctx.lineTo(path[index].x,path[index].y)
                    // index++
                    // if(!path[index]){
                    //     if(count<6){
                    //         count++
                    //         index=0
                    //         path.map(v=>{
                    //             v.x+=40;
                    //             v.y+=40;
                    //         })
                    //         this.ctx.moveTo(path[0].x,path[0].y)
                    //     } else{
                    //         stop()
                    //         this.next()
                    //     }
                    // }
                    // 画重叠线 end
                })
            })
            return this
        }

        var o= new drawingBoard();
        o.init({id: "gycanvas"})
            // .drawCircle({speed: 10, r: 100})
            // .drawLine({speed: 5, x1: 300,y1: 300,x2: 640,y2: 350})
            // .drawHeart({color: 'red', speed: 10, count: 2})
            // .drawStar({speed: 600})
            // .drawStar2({speed: 600,size: 100})
            // .drawAnythingTest({speed: 5, x: 30,y: 100})
            .drawScene()

	</script>
</body>
 
</html>